/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;

namespace STSdb.Data
{
    public class KeyMapBuilder
    {
        public object KeyMap { get; private set; }
        public int Length { get; private set; }

        public KeyMapBuilder(object keyMap, int length)
        {
            KeyMap = keyMap;
            Length = length;
        }

        public static KeyMapBuilder Build(Type keyType, object keyMap, int length)
        {
            KeyMapBuilder builder;
            
            if (keyType == typeof(Boolean))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new BooleanKeyMap(), sizeof(Boolean));
            else if (keyType == typeof(Byte))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new ByteKeyMap(), sizeof(Byte));
            else if (keyType == typeof(Char))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new CharKeyMap(), sizeof(Char));
            else if (keyType == typeof(DateTime))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new DateTimeKeyMap(), sizeof(long));
            else if (keyType == typeof(TimeSpan))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new TimeSpanKeyMap(), sizeof(long));
            else if (keyType == typeof(Decimal))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new DecimalKeyMap(), 15 + 1);
            else if (keyType == typeof(Double))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new DoubleKeyMap(), sizeof(Double));
            else if (keyType == typeof(Int16))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new Int16KeyMap(), sizeof(Int16));
            else if (keyType == typeof(Int32))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new Int32KeyMap(), sizeof(Int32));
            else if (keyType == typeof(Int64))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new Int64KeyMap(), sizeof(Int64));
            else if (keyType == typeof(SByte))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new SByteKeyMap(), sizeof(SByte));
            else if (keyType == typeof(Single))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new SingleKeyMap(), sizeof(Double)); //match with length of DoubleKeyMap()
            else if (keyType == typeof(String))
            {
                StringKeyMap inputKeyMap = (StringKeyMap)keyMap;
                StringKeyMap subKeyMap = inputKeyMap ?? (length == 0 ? new StringKeyMap() : new StringKeyMap(length));
                builder = new KeyMapBuilder(subKeyMap, subKeyMap.MaxLength);
            }
            else if (keyType == typeof(UInt16))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new UInt16KeyMap(), sizeof(UInt16));
            else if (keyType == typeof(UInt32))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new UInt32KeyMap(), sizeof(UInt32));
            else if (keyType == typeof(UInt64))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new UInt64KeyMap(), sizeof(UInt64));
            else if (keyType == typeof(byte[]))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new ByteArrayKeyMap(), length);
            else if (keyType == typeof(Locator))
                builder = new KeyMapBuilder(keyMap != null ? keyMap : (object)new LocatorKeyMap(), length);
            else if (keyType.IsValueType && keyType.StructLayoutAttribute.Value != LayoutKind.Auto)
            {
                Type openType = typeof(StructKeyMap<>);
                Type closeType = typeof(StructKeyMap<>).MakeGenericType(keyType);
                builder = new KeyMapBuilder(keyMap != null ? keyMap : Activator.CreateInstance(closeType), Marshal.SizeOf(closeType));
            }
            else
                throw new NotSupportedException(keyType.ToString());

            return builder;
        }

        public static KeyMapBuilder Build<TSubKey>(IKeyMap<TSubKey> keyMap, int length)
        {
            if (keyMap != null)
            {
                Type keyMapType = keyMap.GetType();
                if (keyMapType != typeof(BooleanKeyMap) &&
                    keyMapType != typeof(ByteKeyMap) &&
                    keyMapType != typeof(CharKeyMap) &&
                    keyMapType != typeof(DateTimeKeyMap) &&
                    keyMapType != typeof(TimeSpanKeyMap) &&
                    keyMapType != typeof(DecimalKeyMap) &&
                    keyMapType != typeof(DoubleKeyMap) &&
                    keyMapType != typeof(Int16KeyMap) &&
                    keyMapType != typeof(Int32KeyMap) &&
                    keyMapType != typeof(Int64KeyMap) &&
                    keyMapType != typeof(SByteKeyMap) &&
                    keyMapType != typeof(SingleKeyMap) &&
                    keyMapType != typeof(StringKeyMap) &&
                    keyMapType != typeof(UInt16KeyMap) &&
                    keyMapType != typeof(UInt32KeyMap) &&
                    keyMapType != typeof(UInt64KeyMap) &&
                    keyMapType != typeof(ByteArrayKeyMap) &&
                    keyMapType != typeof(LocatorKeyMap) &&
                    keyMapType != typeof(StructKeyMap<TSubKey>))
                    return new KeyMapBuilder(keyMap, length); //external key map
            }

            return KeyMapBuilder.Build(typeof(TSubKey), keyMap, length);
        }
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1> : IKeyMap<XKey<TSubKey0, TSubKey1>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));

            byte[] buffer = new byte[MaxLengths[1] + MaxLengths[0]];

            buffer1.CopyTo(buffer, 0);
            buffer0.CopyTo(buffer, MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];

            Buffer.BlockCopy(index, 0, buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);

            return new XKey<TSubKey0, TSubKey1>(subKey0, subKey1);
        }

        #endregion
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1, TSubKey2> : IKeyMap<XKey<TSubKey0, TSubKey1, TSubKey2>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;
        public readonly IKeyMap<TSubKey2> KeyMap2;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1, IKeyMap<TSubKey2> keyMap2, int maxLength2)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            //TSubKey2
            KeyMapBuilder builder2 = KeyMapBuilder.Build<TSubKey2>(keyMap2, maxLength2);
            KeyMap2 = (IKeyMap<TSubKey2>)builder2.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length, builder2.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1,TSubKey2>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1, TSubKey2> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);
            byte[] buffer2 = KeyMap2.Direct(key.SubKey2);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));
            if (buffer2.Length > MaxLengths[2])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey2 > {1} bytes", key.SubKey2, MaxLengths[2]));

            byte[] buffer = new byte[MaxLengths[2] + MaxLengths[1] + MaxLengths[0]];

            buffer2.CopyTo(buffer, 0);
            buffer1.CopyTo(buffer, MaxLengths[2]);
            buffer0.CopyTo(buffer, MaxLengths[2] + MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1, TSubKey2> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];
            byte[] buffer2 = new byte[MaxLengths[2]];

            Buffer.BlockCopy(index, 0, buffer2, 0, buffer2.Length);
            Buffer.BlockCopy(index, MaxLengths[2], buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[2] + MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);
            TSubKey2 subKey2 = KeyMap2.Reverse(buffer2);

            return new XKey<TSubKey0, TSubKey1, TSubKey2>(subKey0, subKey1, subKey2);
        }

        #endregion
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1, TSubKey2, TSubKey3> : IKeyMap<XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;
        public readonly IKeyMap<TSubKey2> KeyMap2;
        public readonly IKeyMap<TSubKey3> KeyMap3;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1, IKeyMap<TSubKey2> keyMap2, int maxLength2, IKeyMap<TSubKey3> keyMap3, int maxLength3)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            //TSubKey2
            KeyMapBuilder builder2 = KeyMapBuilder.Build<TSubKey2>(keyMap2, maxLength2);
            KeyMap2 = (IKeyMap<TSubKey2>)builder2.KeyMap;

            //TSubKey3
            KeyMapBuilder builder3 = KeyMapBuilder.Build<TSubKey3>(keyMap3, maxLength3);
            KeyMap3 = (IKeyMap<TSubKey3>)builder3.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length, builder2.Length, builder3.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0, null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1,TSubKey2,TSubKey3>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);
            byte[] buffer2 = KeyMap2.Direct(key.SubKey2);
            byte[] buffer3 = KeyMap3.Direct(key.SubKey3);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));
            if (buffer2.Length > MaxLengths[2])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey2 > {1} bytes", key.SubKey2, MaxLengths[2]));
            if (buffer3.Length > MaxLengths[3])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey3 > {1} bytes", key.SubKey3, MaxLengths[3]));

            byte[] buffer = new byte[MaxLengths[3] + MaxLengths[2] + MaxLengths[1] + MaxLengths[0]];

            buffer3.CopyTo(buffer, 0);
            buffer2.CopyTo(buffer, MaxLengths[3]);
            buffer1.CopyTo(buffer, MaxLengths[3] + MaxLengths[2]);
            buffer0.CopyTo(buffer, MaxLengths[3] + MaxLengths[2] + MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];
            byte[] buffer2 = new byte[MaxLengths[2]];
            byte[] buffer3 = new byte[MaxLengths[3]];

            Buffer.BlockCopy(index, 0, buffer3, 0, buffer3.Length);
            Buffer.BlockCopy(index, MaxLengths[3], buffer2, 0, buffer2.Length);
            Buffer.BlockCopy(index, MaxLengths[3] + MaxLengths[2], buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[3] + MaxLengths[2] + MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);
            TSubKey2 subKey2 = KeyMap2.Reverse(buffer2);
            TSubKey3 subKey3 = KeyMap3.Reverse(buffer3);

            return new XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3>(subKey0, subKey1, subKey2, subKey3);
        }

        #endregion
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4> : IKeyMap<XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;
        public readonly IKeyMap<TSubKey2> KeyMap2;
        public readonly IKeyMap<TSubKey3> KeyMap3;
        public readonly IKeyMap<TSubKey4> KeyMap4;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1, IKeyMap<TSubKey2> keyMap2, int maxLength2, IKeyMap<TSubKey3> keyMap3, int maxLength3, IKeyMap<TSubKey4> keyMap4, int maxLength4)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            //TSubKey2
            KeyMapBuilder builder2 = KeyMapBuilder.Build<TSubKey2>(keyMap2, maxLength2);
            KeyMap2 = (IKeyMap<TSubKey2>)builder2.KeyMap;

            //TSubKey3
            KeyMapBuilder builder3 = KeyMapBuilder.Build<TSubKey3>(keyMap3, maxLength3);
            KeyMap3 = (IKeyMap<TSubKey3>)builder3.KeyMap;

            //TSubKey4
            KeyMapBuilder builder4 = KeyMapBuilder.Build<TSubKey4>(keyMap4, maxLength4);
            KeyMap4 = (IKeyMap<TSubKey4>)builder4.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length, builder2.Length, builder3.Length, builder4.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0, null, 0, null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1,TSubKey2,TSubKey3,TSubKey4>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);
            byte[] buffer2 = KeyMap2.Direct(key.SubKey2);
            byte[] buffer3 = KeyMap3.Direct(key.SubKey3);
            byte[] buffer4 = KeyMap4.Direct(key.SubKey4);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));
            if (buffer2.Length > MaxLengths[2])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey2 > {1} bytes", key.SubKey2, MaxLengths[2]));
            if (buffer3.Length > MaxLengths[3])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey3 > {1} bytes", key.SubKey3, MaxLengths[3]));
            if (buffer4.Length > MaxLengths[4])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey4 > {1} bytes", key.SubKey4, MaxLengths[4]));

            byte[] buffer = new byte[MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1] + MaxLengths[0]];

            buffer4.CopyTo(buffer, 0);
            buffer3.CopyTo(buffer, MaxLengths[4]);
            buffer2.CopyTo(buffer, MaxLengths[4] + MaxLengths[3]);
            buffer1.CopyTo(buffer, MaxLengths[4] + MaxLengths[3] + MaxLengths[2]);
            buffer0.CopyTo(buffer, MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];
            byte[] buffer2 = new byte[MaxLengths[2]];
            byte[] buffer3 = new byte[MaxLengths[3]];
            byte[] buffer4 = new byte[MaxLengths[4]];

            Buffer.BlockCopy(index, 0, buffer4, 0, buffer4.Length);
            Buffer.BlockCopy(index, MaxLengths[4], buffer3, 0, buffer3.Length);
            Buffer.BlockCopy(index, MaxLengths[4] + MaxLengths[3], buffer2, 0, buffer2.Length);
            Buffer.BlockCopy(index, MaxLengths[4] + MaxLengths[3] + MaxLengths[2], buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);
            TSubKey2 subKey2 = KeyMap2.Reverse(buffer2);
            TSubKey3 subKey3 = KeyMap3.Reverse(buffer3);
            TSubKey4 subKey4 = KeyMap4.Reverse(buffer4);

            return new XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4>(subKey0, subKey1, subKey2, subKey3, subKey4);
        }

        #endregion
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5> : IKeyMap<XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;
        public readonly IKeyMap<TSubKey2> KeyMap2;
        public readonly IKeyMap<TSubKey3> KeyMap3;
        public readonly IKeyMap<TSubKey4> KeyMap4;
        public readonly IKeyMap<TSubKey5> KeyMap5;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1, IKeyMap<TSubKey2> keyMap2, int maxLength2, IKeyMap<TSubKey3> keyMap3, int maxLength3, IKeyMap<TSubKey4> keyMap4, int maxLength4, IKeyMap<TSubKey5> keyMap5, int maxLength5)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            //TSubKey2
            KeyMapBuilder builder2 = KeyMapBuilder.Build<TSubKey2>(keyMap2, maxLength2);
            KeyMap2 = (IKeyMap<TSubKey2>)builder2.KeyMap;

            //TSubKey3
            KeyMapBuilder builder3 = KeyMapBuilder.Build<TSubKey3>(keyMap3, maxLength3);
            KeyMap3 = (IKeyMap<TSubKey3>)builder3.KeyMap;

            //TSubKey4
            KeyMapBuilder builder4 = KeyMapBuilder.Build<TSubKey4>(keyMap4, maxLength4);
            KeyMap4 = (IKeyMap<TSubKey4>)builder4.KeyMap;

            //TSubKey5
            KeyMapBuilder builder5 = KeyMapBuilder.Build<TSubKey5>(keyMap5, maxLength5);
            KeyMap5 = (IKeyMap<TSubKey5>)builder5.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length, builder2.Length, builder3.Length, builder4.Length, builder5.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0, null, 0, null, 0, null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1,TSubKey2,TSubKey3,TSubKey4,TSubKey5>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);
            byte[] buffer2 = KeyMap2.Direct(key.SubKey2);
            byte[] buffer3 = KeyMap3.Direct(key.SubKey3);
            byte[] buffer4 = KeyMap4.Direct(key.SubKey4);
            byte[] buffer5 = KeyMap5.Direct(key.SubKey5);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));
            if (buffer2.Length > MaxLengths[2])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey2 > {1} bytes", key.SubKey2, MaxLengths[2]));
            if (buffer3.Length > MaxLengths[3])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey3 > {1} bytes", key.SubKey3, MaxLengths[3]));
            if (buffer4.Length > MaxLengths[4])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey4 > {1} bytes", key.SubKey4, MaxLengths[4]));
            if (buffer5.Length > MaxLengths[5])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey5 > {1} bytes", key.SubKey5, MaxLengths[5]));

            byte[] buffer = new byte[MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1] + MaxLengths[0]];

            buffer5.CopyTo(buffer, 0);
            buffer4.CopyTo(buffer, MaxLengths[5]);
            buffer3.CopyTo(buffer, MaxLengths[5] + MaxLengths[4]);
            buffer2.CopyTo(buffer, MaxLengths[5] + MaxLengths[4] + MaxLengths[3]);
            buffer1.CopyTo(buffer, MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2]);
            buffer0.CopyTo(buffer, MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];
            byte[] buffer2 = new byte[MaxLengths[2]];
            byte[] buffer3 = new byte[MaxLengths[3]];
            byte[] buffer4 = new byte[MaxLengths[4]];
            byte[] buffer5 = new byte[MaxLengths[5]];

            Buffer.BlockCopy(index, 0, buffer5, 0, buffer5.Length);
            Buffer.BlockCopy(index, MaxLengths[5], buffer4, 0, buffer4.Length);
            Buffer.BlockCopy(index, MaxLengths[5] + MaxLengths[4], buffer3, 0, buffer3.Length);
            Buffer.BlockCopy(index, MaxLengths[5] + MaxLengths[4] + MaxLengths[3], buffer2, 0, buffer2.Length);
            Buffer.BlockCopy(index, MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2], buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);
            TSubKey2 subKey2 = KeyMap2.Reverse(buffer2);
            TSubKey3 subKey3 = KeyMap3.Reverse(buffer3);
            TSubKey4 subKey4 = KeyMap4.Reverse(buffer4);
            TSubKey5 subKey5 = KeyMap5.Reverse(buffer5);

            return new XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5>(subKey0, subKey1, subKey2, subKey3, subKey4, subKey5);
        }

        #endregion
    }

    [Serializable]
    public class XKeyMap<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5, TSubKey6> : IKeyMap<XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5, TSubKey6>>
    {
        public readonly IKeyMap<TSubKey0> KeyMap0;
        public readonly IKeyMap<TSubKey1> KeyMap1;
        public readonly IKeyMap<TSubKey2> KeyMap2;
        public readonly IKeyMap<TSubKey3> KeyMap3;
        public readonly IKeyMap<TSubKey4> KeyMap4;
        public readonly IKeyMap<TSubKey5> KeyMap5;
        public readonly IKeyMap<TSubKey6> KeyMap6;

        public readonly int[] MaxLengths;

        public XKeyMap(IKeyMap<TSubKey0> keyMap0, int maxLength0, IKeyMap<TSubKey1> keyMap1, int maxLength1, IKeyMap<TSubKey2> keyMap2, int maxLength2, IKeyMap<TSubKey3> keyMap3, int maxLength3, IKeyMap<TSubKey4> keyMap4, int maxLength4, IKeyMap<TSubKey5> keyMap5, int maxLength5, IKeyMap<TSubKey6> keyMap6, int maxLength6)
        {
            //TSubKey0
            KeyMapBuilder builder0 = KeyMapBuilder.Build<TSubKey0>(keyMap0, maxLength0);
            KeyMap0 = (IKeyMap<TSubKey0>)builder0.KeyMap;

            //TSubKey1
            KeyMapBuilder builder1 = KeyMapBuilder.Build<TSubKey1>(keyMap1, maxLength1);
            KeyMap1 = (IKeyMap<TSubKey1>)builder1.KeyMap;

            //TSubKey2
            KeyMapBuilder builder2 = KeyMapBuilder.Build<TSubKey2>(keyMap2, maxLength2);
            KeyMap2 = (IKeyMap<TSubKey2>)builder2.KeyMap;

            //TSubKey3
            KeyMapBuilder builder3 = KeyMapBuilder.Build<TSubKey3>(keyMap3, maxLength3);
            KeyMap3 = (IKeyMap<TSubKey3>)builder3.KeyMap;

            //TSubKey4
            KeyMapBuilder builder4 = KeyMapBuilder.Build<TSubKey4>(keyMap4, maxLength4);
            KeyMap4 = (IKeyMap<TSubKey4>)builder4.KeyMap;

            //TSubKey5
            KeyMapBuilder builder5 = KeyMapBuilder.Build<TSubKey5>(keyMap5, maxLength5);
            KeyMap5 = (IKeyMap<TSubKey5>)builder5.KeyMap;

            //TSubKey6
            KeyMapBuilder builder6 = KeyMapBuilder.Build<TSubKey6>(keyMap6, maxLength6);
            KeyMap6 = (IKeyMap<TSubKey6>)builder6.KeyMap;

            MaxLengths = new int[] { builder0.Length, builder1.Length, builder2.Length, builder3.Length, builder4.Length, builder5.Length, builder6.Length };
        }

        public XKeyMap()
            : this(null, 0, null, 0, null, 0, null, 0, null, 0, null, 0, null, 0)
        {
        }

        #region IKeyMap<XKey<TSubKey0,TSubKey1,TSubKey2,TSubKey3,TSubKey4,TSubKey5,TSubKey6>> Members

        public byte[] Direct(XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5, TSubKey6> key)
        {
            byte[] buffer0 = KeyMap0.Direct(key.SubKey0);
            byte[] buffer1 = KeyMap1.Direct(key.SubKey1);
            byte[] buffer2 = KeyMap2.Direct(key.SubKey2);
            byte[] buffer3 = KeyMap3.Direct(key.SubKey3);
            byte[] buffer4 = KeyMap4.Direct(key.SubKey4);
            byte[] buffer5 = KeyMap5.Direct(key.SubKey5);
            byte[] buffer6 = KeyMap6.Direct(key.SubKey6);

            if (buffer0.Length > MaxLengths[0])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey0 > {1} bytes", key.SubKey0, MaxLengths[0]));
            if (buffer1.Length > MaxLengths[1])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey1 > {1} bytes", key.SubKey1, MaxLengths[1]));
            if (buffer2.Length > MaxLengths[2])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey2 > {1} bytes", key.SubKey2, MaxLengths[2]));
            if (buffer3.Length > MaxLengths[3])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey3 > {1} bytes", key.SubKey3, MaxLengths[3]));
            if (buffer4.Length > MaxLengths[4])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey4 > {1} bytes", key.SubKey4, MaxLengths[4]));
            if (buffer5.Length > MaxLengths[5])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey5 > {1} bytes", key.SubKey5, MaxLengths[5]));
            if (buffer6.Length > MaxLengths[6])
                throw new IndexOutOfRangeException(String.Format("sizeof({0}) of type TSubKey6 > {1} bytes", key.SubKey6, MaxLengths[6]));

            byte[] buffer = new byte[MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1] + MaxLengths[0]];

            buffer6.CopyTo(buffer, 0);
            buffer5.CopyTo(buffer, MaxLengths[6]);
            buffer4.CopyTo(buffer, MaxLengths[6] + MaxLengths[5]);
            buffer3.CopyTo(buffer, MaxLengths[6] + MaxLengths[5] + MaxLengths[4]);
            buffer2.CopyTo(buffer, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3]);
            buffer1.CopyTo(buffer, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2]);
            buffer0.CopyTo(buffer, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1]);

            return buffer;
        }

        public XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5, TSubKey6> Reverse(byte[] index)
        {
            byte[] buffer0 = new byte[MaxLengths[0]];
            byte[] buffer1 = new byte[MaxLengths[1]];
            byte[] buffer2 = new byte[MaxLengths[2]];
            byte[] buffer3 = new byte[MaxLengths[3]];
            byte[] buffer4 = new byte[MaxLengths[4]];
            byte[] buffer5 = new byte[MaxLengths[5]];
            byte[] buffer6 = new byte[MaxLengths[6]];

            Buffer.BlockCopy(index, 0, buffer6, 0, buffer6.Length);
            Buffer.BlockCopy(index, MaxLengths[6], buffer5, 0, buffer5.Length);
            Buffer.BlockCopy(index, MaxLengths[6] + MaxLengths[5], buffer4, 0, buffer4.Length);
            Buffer.BlockCopy(index, MaxLengths[6] + MaxLengths[5] + MaxLengths[4], buffer3, 0, buffer3.Length);
            Buffer.BlockCopy(index, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3], buffer2, 0, buffer2.Length);
            Buffer.BlockCopy(index, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2], buffer1, 0, buffer1.Length);
            Buffer.BlockCopy(index, MaxLengths[6] + MaxLengths[5] + MaxLengths[4] + MaxLengths[3] + MaxLengths[2] + MaxLengths[1], buffer0, 0, buffer0.Length);

            TSubKey0 subKey0 = KeyMap0.Reverse(buffer0);
            TSubKey1 subKey1 = KeyMap1.Reverse(buffer1);
            TSubKey2 subKey2 = KeyMap2.Reverse(buffer2);
            TSubKey3 subKey3 = KeyMap3.Reverse(buffer3);
            TSubKey4 subKey4 = KeyMap4.Reverse(buffer4);
            TSubKey5 subKey5 = KeyMap5.Reverse(buffer5);
            TSubKey6 subKey6 = KeyMap6.Reverse(buffer6);

            return new XKey<TSubKey0, TSubKey1, TSubKey2, TSubKey3, TSubKey4, TSubKey5, TSubKey6>(subKey0, subKey1, subKey2, subKey3, subKey4, subKey5, subKey6);
        }

        #endregion
    }
}
